iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Modern Web

就是要搞懂 JavaScript 啦!系列 第 9

Day09 這是我的地盤──函式作用域

  • 分享至 

  • xImage
  •  

函式作用域(Function Scope)

JS 的每個函式都擁有自己的作用域,可以說,當你宣告了一個函式,同時就創建了一個新的作用域。

函式作用域的規則如下:

  • 每個函式都會創造自己的作用域,其內的變數皆屬於該函式
  • 函式作用域的識別字,只能在這個函式內使用
  • 對外部隱藏自己的作用域,以保護內部變數不被隨意存取
function foo(a) {
	var b = 2;

	function bar() {
		// ...
	}

	console.log( a, b, c ); // 1 2 undefined
    var c = 3;
	bar(); // 成功

}
foo(1);
bar(); // ReferenceError
console.log( a, b, c ); // 3个都失败,回報 ReferenceError

說完函式作用域的基礎規則後,下面來看看不同種類的函式,以及它們的作用域如何運作。


具名 v.s. 匿名函式

  • 具名函式(Named Function):擁有名字(識別字)的函式
  • 匿名函式(Anonymous Function):沒有名字(識別字)的函式

匿名函式範例:

setTimeout( function(){
	console.log("I waited 1 second!");
}, 1000 );

匿名函式的缺點

  • stack trace 報錯時沒有具體名稱,較難追蹤錯誤。
  • 沒有名稱的函數難以進行遞迴。
  • 事件處理函式被調用後,無法指定名稱解除綁定。
  • 失去具有描述性的名稱,無法立即知道該函式的功能,可讀性較差。

函式宣告 v.s. 函式表達式

  • 函式宣告(Function Declaration)
    • function 作為語句開頭的函式
    • 函式的識別字綁定在外圍作用域
    • 無法匿名,必須以識別字宣告
  • 函式表達式(Function Expression)
    • 並非以 function 作為語句開頭的函式
    • 函式的識別字綁定在表達式內部的作用域
    • 可以匿名,不需要給予識別字
var a = 2;

(function foo(){ // <-- 插入 "("

	var a = 3;
	console.log( a ); // 3

})(); // <-- 插入 ")()"

foo(); // ReferenceError
console.log( a ); // 2

如以上範例,函式 foo 在定義時並非以 function 作為語句開頭,因此是一段函式表達式,函式識別字 foo 以及內部變數都屬於函式內部,無法從外部存取。

函式宣告的規則可以直接參考文章最開頭的敘述通則,以下來討論函式表達式要注意的部分。

行內函式表達式 Inline function expressions

將一個匿名函式賦值給一個變數,與函式宣告的不同之處和提升(Hoisting)有關。

函式宣告的內容會被提升,但函式表達式並沒有提升,之後關於提升的文章將詳細討論這一點。

var func = function() { 
    //Your Code Here 
};

即刻調用函式表達式(Immediately Invoked Function Expression, IIFE)

在原地馬上進行調用的函式,可具名也可匿名,但即便是具名函式也無法被外部作用域取得,調用完畢後會立刻被回收。

var a = 2;

(function foo(){
	var a = 3;
	console.log( a ); // 3
	console.log( foo ); // ƒ foo(){ ... }
})();

console.log( a ); // 2
console.log( foo ); // ReferenceError: foo is not defined

IIFE 的另一種寫法:(function(){ .. }()),兩者的差異只有調用的括號所在位置。

IIFE 的其他用法

由於 IIFE 擁有只屬於自己的作用域,以及用後立即回收的特性,它也是經常被使用的一種函式。

指定範疇:將傳入的參數當作作用域

var a = 2;

(function IIFE(global) {
  var a = 3;
  console.log(a); // 3
  console.log(global.a); // 2
})(window);

console.log(a); // 2

確保 undefined 的正確性

undefined = true;

(function IIFE(undefined) {
  var a;
  if (a === undefined) {
    console.log('Undefined 在這裡很安全!');
  }
})();

反轉順序
如通用模組定義(UMD, Universal Module Definition)

var a = 2;

(function IIFE(def) {
  def(window);
})(function def(global) {
  var a = 3;
  console.log(a); // 3
  console.log(global.a); // 2
});

參考資料


上一篇
Day08 宣告領土──詞法作用域
下一篇
Day10 我也有自己的地盤啦!──區塊作用域
系列文
就是要搞懂 JavaScript 啦!73
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言